# -*- coding: UTF-8 -*-
'''
Created on 02.09.2014

@author: MathiasAr
'''
from random import WichmannHill
import sys
import time
import thread

import wx
import gettext
from wx.lib.delayedresult import AbortEvent
from ImageHelper import IsImageFile, GetImageOrientation, SetOrAddKey
from MediaHelper import IsSupportedFile, MediaFiles
from wxGui import ImageLoader
from wxGui.ImageLoader import WxAsyncImageLoader

from wxMediaBrowser import wxMain
from wx import FileDialog, DirDialog, DD_DEFAULT_STYLE
import os


class WxBaseFrame(wxMain):
    EVT_SCAN_PATH_ID = wx.NewId()

    def __init__(self, *args, **kwargs):
        """
        :param args:
        :param kwargs:
        :return:
        """
        wxMain.__init__(self, *args, **kwargs)

        # initialize internal members
        self._media_folder = ''
        self._media_db = MediaFiles()
        self._image_loader = ImageLoader.WxAsyncImageLoader
        self._preview_size = [50, 50]
        self._mini_previews = {}
        self._mini_previews_lock = thread.allocate_lock()
        self._mini_selected = -1

        # initialize gui elements
        self.list_ctrl_mediaFiles.InsertColumn(0, "File name")
        self.list_ctrl_mediaFiles.InsertColumn(1, "Meta date")
        self.list_ctrl_mediaFiles.InsertColumn(2, "Modified")
        self.list_ctrl_mediaFiles.InsertColumn(3, "Size")
        self.list_ctrl_mediaFiles.InsertColumn(4, "Orient.")
        self.list_ctrl_mediaFiles.InsertColumn(5, "Make/Model")

        # register events
        self.Connect(-1, -1, self.EVT_SCAN_PATH_ID, self._onPathScanEvent)
        pass

    def _stop_image_loader(self):
        if isinstance(self._image_loader, ImageLoader.WxAsyncImageLoader):
            self._image_loader.quit()
        self._image_loader = None

    def _onMediaFileSelect(self, event):
        item = event.Item.Text
        file_name = os.path.join(self._media_folder, item)
        loader = WxAsyncImageLoader(image_size=(50, 50), crop_image=True, quality=wx.IMAGE_QUALITY_NORMAL)
        loader.connect(self, self._onMiniPreviewLoaded, item)
        # take a lock on the mini previews
        self._mini_previews_lock.acquire()
        # append the item to the list without GUI element
        SetOrAddKey(self._mini_previews, item, None)
        # show the selected count
        self.label_mini_preview.SetLabelText('%s file(s) selected' % len(self._mini_previews))
        # unlock
        self._mini_previews_lock.release()
        # start loading image
        loader.start((file_name, ))
        pass

    def _onMiniPreviewLoaded(self, event):
        im, item = event.data
        # take a lock on the mini previews
        self._mini_previews_lock.acquire()
        # item deselected while image loaded?
        if item in self._mini_previews:
            # item already selected --> make the bitmap and GUI element
            static_bmp = wx.BitmapButton(self.panel_1, style=wx.NO_BORDER)
            static_bmp.Hide()
            static_bmp.SetBitmap(im.ConvertToBitmap())
            static_bmp.SetLabelText(item)
            self.Bind(wx.EVT_BUTTON, self.onMiniClicked, static_bmp)
            # remind
            SetOrAddKey(self._mini_previews, item, static_bmp)
            # fit the sizer
            self._fitPreviewSizer()
            # append item
            self.grid_sizer_previews.Add(static_bmp, proportion=0, flag=0, border=0, userData=None)
            self.grid_sizer_previews.Layout()
            static_bmp.Show()
            self.Refresh()
        # unlock
        self._mini_previews_lock.release()

    def _onMediaFileDeselect(self, event):
        item = event.Item.Text
        file_name = os.path.join(self._media_folder, item)
        # take a lock on the mini previews
        self._mini_previews_lock.acquire()
        # remove item if already added
        if item in self._mini_previews:
            # remove list entry
            static_bmp = self._mini_previews.pop(item)
            # check if image loader has properly loaded the item
            if static_bmp is not None:
                try:
                    # remove GUI element
                    assert isinstance(static_bmp, wx.BitmapButton)
                    self.grid_sizer_previews.Detach(static_bmp)
                    self.Unbind(wx.EVT_BUTTON, static_bmp)
                    static_bmp.Destroy()
                    self.grid_sizer_previews.Layout()
                except AssertionError:
                    pass
        # show the selected count
        self.label_mini_preview.SetLabelText('%s file(s) selected' % len(self._mini_previews))
        # unlock
        self._mini_previews_lock.release()
        pass

    def _fitPreviewSizer(self, clear=False):
        if clear:
            for item in self._mini_previews:
                static_bitmap = self._mini_previews[item]
                assert isinstance(static_bitmap, wx.BitmapButton)
                static_bitmap.Hide()
            self.grid_sizer_previews.Clear()
        width, height = self.window_1_pane_2.GetSize()
        col = max(width / 54, 1)
        row = max(height / 54, 1)
        n = len(self._mini_previews)
        row = max(int(round((float(n) / float(col)) + 0.5)), row)
        old_col = self.grid_sizer_previews.GetCols()
        old_row = self.grid_sizer_previews.GetRows()
        if col < old_col:
            self.grid_sizer_previews.SetRows(row)
            self.grid_sizer_previews.SetCols(col)
        elif row != old_row or col != old_col:
            self.grid_sizer_previews.SetCols(col)
            self.grid_sizer_previews.SetRows(row)
        # print 'items = %s / width x height = %s x %s --> col = %s --> row = %s' % (n, width, height, col, row)
        if clear:
            for item in self._mini_previews:
                static_bitmap = self._mini_previews[item]
                assert isinstance(static_bitmap, wx.StaticBitmap)
                self.grid_sizer_previews.Add(static_bitmap)
                static_bitmap.Show()
        self.grid_sizer_previews.Layout()

    def onMiniClicked(self, event):
        item = event.EventObject
        item_index = self.grid_sizer_previews.GetItemIndex(item)
        self._openSelectedMiniPreview(item_index)
        pass

    def _openSelectedMiniPreview(self, index):
        item_name = 'Unknown'
        try:
            self._mini_previews_lock.acquire()
            static_bmp = self.grid_sizer_previews.GetItem(index)
            assert isinstance(static_bmp, wx.SizerItem)
            static_bmp = static_bmp.GetWindow()
            assert isinstance(static_bmp, wx.BitmapButton)

            item_name = static_bmp.GetLabelText()
            if item_name in self._mini_previews:
                item = self._mini_previews[item_name]
                if self._mini_selected >= 0:
                    lastone = self.grid_sizer_previews.GetItem(self._mini_selected)
                    assert isinstance(lastone, wx.SizerItem)
                    lastone = lastone.GetWindow()
                    assert isinstance(lastone, wx.BitmapButton)
                    lastone.SetWindowStyle(wx.BORDER_NONE)
                self._mini_selected = self.grid_sizer_previews.GetItemIndex(item)
                static_bmp.SetWindowStyle(wx.BORDER_DOUBLE)
                self.grid_sizer_previews.Layout()
                # load the image
                self._stop_image_loader()
                self._image_loader = WxAsyncImageLoader(
                    image_size=self.bitmap_preview.GetSize(),
                    crop_image=False,
                    quality=wx.IMAGE_QUALITY_HIGH)
                self._image_loader.connect(self, self._onPreviewImageLoaded, item_name)
                file_name = os.path.join(self._media_folder, item_name)
                self._image_loader.start((file_name, ))
                pass
        except AssertionError:
            sys.stderr.write('Error while processing item %s --> %s' % (item, sys.exc_value))
            self.bitmap_preview.ClearBackground()
            pass
        self._mini_previews_lock.release()
        pass

    def _onPreviewImageLoaded(self, event):
        im, item_name = event.data
        self.bitmap_preview.SetBitmap(im.ConvertToBitmap())
        self.bitmap_preview.Layout()
        self.sizer_18.Layout()
        self.sizer_18.Fit(self.bitmap_preview)
        self.window_1_pane_1.Layout()
        self.Layout()
        self.Refresh()
        pass

    def _onPathScanEvent(self, event):
        f = ''
        files = []
        if isinstance(event.data, list):
            # scan finished
            self.label_status.SetLabelText('%s file(s) found' % len(event.data))
            # enable folder change
            self.button_selectMediaFolder.SetEvtHandlerEnabled(True)
            self.text_ctrl_mediaFolder.SetEditable(True)
            pass
        elif isinstance(event.data, basestring):
            # add to list control
            file_name = event.data
            if self._media_db is not None:
                # short the path by relative format
                rel_path = os.path.relpath(file_name, self._media_folder)
                self.label_status.SetLabelText('Found: %s' % rel_path)
                # get media information
                model = self._media_db.get_model(file_name)
                meta_time = self._media_db.get_datetime(file_name)
                modified = self._media_db.get_modified(file_name)
                w, h = self._media_db.get_size(file_name)
                if w is not None and h is not None:
                    w_h = '%s x %s' % (w, h)
                else:
                    w_h = ''
                # get the image orientation
                exif_orient = ''
                if IsImageFile(file_name):
                    orient = GetImageOrientation(file_name)
                    if orient != 0:
                        exif_orient = '%s' % orient
                # append into table
                self.list_ctrl_mediaFiles.Append(
                    (rel_path,
                     meta_time.ctime(),
                     modified.ctime(),
                     w_h,
                     exif_orient,
                     model)
                )
        pass

    def onCloseBtn(self, event):  # wxGlade: wxMain.<event_handler>
        self.Close()

    def onMediaFolderChanged(self, event):  # wxGlade: wxMain.<event_handler>
        media_path = self.text_ctrl_mediaFolder.GetValue()
        self.list_ctrl_mediaFiles.DeleteAllItems()
        self._media_db = MediaFiles()
        self._media_folder = None

        if os.path.isdir(media_path):
            self._media_folder = media_path
            self.text_ctrl_mediaFolder.SetBackgroundColour(wx.NullColour)
            self.button_selectMediaFolder.SetEvtHandlerEnabled(False)
            self.text_ctrl_mediaFolder.SetEditable(False)
            thread.start_new_thread(self._path_scan_thread, (media_path, ))
        else:
            self.text_ctrl_mediaFolder.SetBackgroundColour(wx.RED)
            self.button_selectMediaFolder.SetEvtHandlerEnabled(True)
            self.text_ctrl_mediaFolder.SetEditable(True)

        self.Refresh()


    def onMediaFolderButton(self, event):  # wxGlade: wxMain.<event_handler>
        openDialog = DirDialog(self, message='Select a folder...', defaultPath='',
                               style=wx.DD_DIR_MUST_EXIST | wx.DD_NEW_DIR_BUTTON)
        result = openDialog.ShowModal()

        # check dialog result
        if result == wx.ID_OK:
            self.text_ctrl_mediaFolder.SetValue(openDialog.GetPath())
            pass

    def _path_scan_thread(self, root_path):

        def get_dir(path, result):
            try:
                list_dir = os.listdir(path)
            except:
                return result
            for s in list_dir:
                # check if main window is already shown
                if not self.IsShown():
                    raise AbortEvent('Aborted file scan thread')
                s = os.path.join(path, s)
                if os.path.isdir(s):
                    # give other threads time
                    time.sleep(0.01)
                    result = get_dir(s, result)
                elif IsSupportedFile(s):
                    if self._media_db is not None and self._media_db.append(s):
                        result.append(s)
                        _path_event = wx.PyEvent()
                        _path_event.SetEventType(self.EVT_SCAN_PATH_ID)
                        _path_event.data = s
                        wx.PostEvent(self, _path_event)
            return result

        # scan all given path directories
        try:
            files = get_dir(root_path, [])
        except AbortEvent:
            return
        time.sleep(0.1)
        _path_event = wx.PyEvent()
        _path_event.SetEventType(self.EVT_SCAN_PATH_ID)
        _path_event.data = files
        wx.PostEvent(self, _path_event)
        pass

    def _onPreviewPlus(self, event):
        pass

    def _onRotateRight(self, event):
        pass

    def _onNextPreview(self, event):
        if self._mini_selected < self.grid_sizer_previews.GetItemCount()-1:
            self._openSelectedMiniPreview(self._mini_selected+1)
        pass

    def _onPrevPreview(self, event):
        if self._mini_selected > 0:
            self._openSelectedMiniPreview(self._mini_selected-1)
        pass

    def _onRotateLeft(self, event):
        pass

    def _onPreviewMinus(self, event):
        pass


class wxBaseApp(wx.App):
    def OnInit(self):
        wx.InitAllImageHandlers()
        frame_1 = WxBaseFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(frame_1)
        frame_1.Show()
        return 1


if __name__ == '__main__':
    gettext.install("app")  # replace with the appropriate catalog name

    app = wxBaseApp()
    app.MainLoop()
    
